/**
 * The Calendar Class support the Pop.Ajax.Calendar WebControl
 * TODO: Add support for Discreet Date Selection
 */
var Calendar = Class.create(/** @lends Calendar.prototype */ {
	/**
	 * Constructor
	 * @constructs
	 * @param {String} sElementId The ID of the MiniCal table
	 * @param {String} sUniqueID The Asp.Net UniqueID of the MiniCal control
	 * @param {Object} oOptions Calendar Options
	 * Options include:
	 *     selectionType: [ day | range | discreet ]
	 *     hasPreRequestEvents: [ true | false ]
	 */
	initialize: function(sElementId, sUniqueID, oOptions) {
		// Find the calendar table and bind to its click events
		this.table = $(sElementId);
		var oOptions = oOptions || {selectionType: "day", hasPreRequestEvents: false}

		// PostBack __EVENTTARGET will be the calendar table
		this.target = sUniqueID;
		this.postBackOptions = {
			target: this.target,
			before: (oOptions.hasPreRequestEvents) ? this.getEventKey("beforerequest") : false ,
			after: this.getEventKey('afterrequest'),
			calendar: this
		};
		
		// Initialize "state" variables
		this.selectionType = oOptions.selectionType;
		this.date1;
		this.date2;
		this.selectCount = 0;
		
		this.table.observe('click', this.__Click.bind(this));
	},
	/**
	 * Event handler for for all Click events within the table
	 * @param {DOMEvent} e The DOMEvent which is bubbling through the table
	 */
	__Click: function(e) {
		var element = e.element();
		if (element.nodeName != "A") {
			// if clicked element isn't an a, and none of its parents are a's, don't handle it
			element = element.up("a");
			if (!element) {
				return;
			}
		}
		var arg = element.href.toQueryParams()['__EVENTARGUMENT'];
		if (arg != null) {
			// Day selector argument is in the form of "00/00/0000"
			var isDaySelector = arg.match(/^\d+\/\d+\/\d+$/) != null;
			// Nav link argument is in the form of "V00/0000"
			var isNavLink = arg.match(/^V\d+\/\d+$/gi) != null;
			// If "range" selection type is enabled and the current request IS from a calendar day
			if (this.selectionType == "range" && isDaySelector) {
				this.selectCount++;
				// Store date in correct variable depening on 1st or 2nd date selection
				this["date" + ((this.selectCount % 2) + 1)] = arg;
				// Update the day cell class name
				element.up().addClassName("range");
				if (this.selectCount % 2 == 0) {
					// If this is the second click, post the date range request
					this.selectDates(this.date1 + "-" + this.date2, e);
				} else {
					e.stop();
				}
			} else if (isDaySelector) {
				this.selectDates(arg, e);
			} else if (isNavLink) {
				this.showMonth(arg, e);
			}
		}
	},
	_eventKeys: ["beforeselect", "beforechange", "beforerequest", "afterrequest"],
	/**
	 * Gets the custom event key for the given event name
	 * @param {String} sEventName The name of the event
	 * @return {String} The fully qualified custom event key
	 * @example
	 * beforeselect, beforechange, beforerequest, afterrequest
	 */
	getEventKey: function(sEventName){
		if (this._eventKeys.indexOf(sEventName) == -1) {
			var msg = new Template("Invalid Calendar event key '#{key}'. Key must be one of #{keys}");
			alert(new CalendarException(msg.evaluate({key: sEventName, keys: this._eventKeys.toString()})).toString());
		}
		return this.table.id + ":" + sEventName;
	},
	/**
	 * Select a date, date range, or selection of discreet dates
	 * @param {String} sDates The date collection to select
	 * @param {DOMEvent} e The DOM Event that initiated the request, if any
	 * @return void
	 * @example
	 * Single Day: selectDates("5/5/2008");
	 * Range: selectDates("5/5/2008-5/10/2008");
	 * Discreet single-day: selectDates("X5/5/2008");
	 * Discreet multi-day: selectDates("5/5/2008;5/8/2008");
	 */
	selectDates: function(sDates, e) {
		// the element is the Event element or the calendar table
		var element = (e) ? e.element() : this.table ;
		// "00/00/0000-00/00/0000"
		var isRangeRequest = sDates.indexOf("-") > -1;
		// "00/00/0000;00/00/0000 or X00/00/0000"
		var isMultiSelect = sDates.indexOf(";") > -1 || sDates.match(/^X\d/) != null;
		// "00/00/0000"
		var isSingleSelect = sDates.match(/^\d+\/\d+\/\d+$/) != null;
		
		var arg;
		if (isRangeRequest) {
			// replace dash with semi-colon and prepend "R" range command
			arg = sDates.replace(/-/,";").replace(/(^\d+)/,"R$1");
		} else if (isMultiSelect) {
			// prepend "X" multi date command if necessary
			arg = sDates.replace(/(^\d+)/,"X$1");
		} else if (isSingleSelect) {
			arg = sDates;
		} else {
			return; // don't do anything if invalid selection string
		}
		
		this.postBackOptions = Object.extend(this.postBackOptions, {argument: arg});
		
		// Fire any pre-selection event handlers
		var before = this.table.fire(
			this.getEventKey('beforeselect'),
			this.postBackOptions
		);
		
		this.postBackOptions = Object.extend(this.postBackOptions, before.memo);
		
		// Make the request
		this.__post(e, this.postBackOptions);
	},
	/**
	 * Shows the specified month
	 * @param {String} sMonth The month to show; format 00/0000
	 * @param {DOMEvent} e The DOM Event that initiated the request, if any
	 * @return void
	 */
	showMonth: function(sMonth, e) {
		// the element is the Event element or the calendar table
		var element = (e) ? e.element() : this.table ;
		
		this.postBackOptions = Object.extend(this.postBackOptions, {argument: sMonth});

		// Fire any pre-selection event handlers
		var before = this.table.fire(
			this.getEventKey('beforechange'),
			this.postBackOptions
		);
		
		this.postBackOptions = Object.extend(this.postBackOptions, before.memo);

		// Make the request
		this.__post(e, this.postBackOptions);
	},
	/**
	 * Posts the mini cal request to the server
	 * @param {DOMEvent} e The DOM Event that initiated the request, if any
	 * @param {Object} oOptions PostBack options to pass to the request
	 * @return void
	 */
	__post: function(e, oOptions) {
		Page.post(e || false,  oOptions);
	}
});

var CalendarException = Class.create({
	initialize: function(message) {
		this.message = message;
	},
	toString: function() {
		return "CalendarException: " + this.message;
	}
});